bitkeeper revision 1.999.1.3 (40dab630QByApr7LI3yKlcPaKnlz6w)
authormjw@wray-m-3.hpl.hp.com <mjw@wray-m-3.hpl.hp.com>
Thu, 24 Jun 2004 11:08:32 +0000 (11:08 +0000)
committermjw@wray-m-3.hpl.hp.com <mjw@wray-m-3.hpl.hp.com>
Thu, 24 Jun 2004 11:08:32 +0000 (11:08 +0000)
Restructure xm, and command grouping.
Better support for xend restart and device destruction.

15 files changed:
docs/xen_config.html
tools/xenmgr/lib/EventTypes.py
tools/xenmgr/lib/XendClient.py
tools/xenmgr/lib/XendDomain.py
tools/xenmgr/lib/XendDomainInfo.py
tools/xenmgr/lib/server/SrvConsoleServer.py
tools/xenmgr/lib/server/SrvDomain.py
tools/xenmgr/lib/server/blkif.py
tools/xenmgr/lib/server/controller.py
tools/xenmgr/lib/server/messages.py
tools/xenmgr/lib/server/netif.py
tools/xenmgr/lib/xm/create.py
tools/xenmgr/lib/xm/main.py
tools/xenmgr/lib/xm/opts.py
tools/xenmgr/lib/xm/shutdown.py

index 0a919cc25df8da11401c30d9f793b41a701ecab6..028dd569b3046f380c9410bf500e9e63b98dece4 100644 (file)
@@ -86,13 +86,13 @@ Defines a netbsd kernel image and its command-line parameters.
     <li>args: string, optional. Extra kernel args.
 </ul>
 
-<h2>(controller (block)) element</h2>
-Define that the vm is a block device controller backend.
+<h2>(backend (blkif)) element</h2>
+The vm is a block device backend.
 The vm can have pci devices configured, but no virtual
 block devices.
 
-<h2>(controller (net)) element</h2>
-Define that the vm is a net device controller backend.
+<h2>(backend (netif)) element</h2>
+The vm is a net device backend.
 The domain may not have virtual network interfaces (vifs) configured.
 
 <h2>(device (vif)) element</h2>
index c8d5e62aa70c35723ff7414852314c34ed312cef..6350baa5ddbfc13c03bc3f08e7bbb16609123bed 100644 (file)
@@ -8,7 +8,7 @@
 ## xend.domain.unpause: dom
 ## xend.domain.pause: dom
 ## xend.domain.shutdown: dom
-## xend.domain.halt: dom
+## xend.domain.destroy: dom
 
 ## xend.domain.migrate.begin: dom, to
 ## Begin tells: src host, src domain uri, dst host. Dst id known?
index fb4a5b4421c7f3018f1552bc585167b2a42b4d6c..664835a1550db90c9925ddd676071c445dcc47a7 100644 (file)
@@ -199,9 +199,9 @@ class Xend:
         return xend_call(self.domainurl(id),
                          {'op'      : 'shutdown'})
 
-    def xend_domain_halt(self, id):
+    def xend_domain_destroy(self, id):
         return xend_call(self.domainurl(id),
-                         {'op'      : 'halt'})
+                         {'op'      : 'destroy'})
 
     def xend_domain_save(self, id, filename):
         return xend_call(self.domainurl(id),
index f236a43d8094fe67b2642d62b15a1a142c7c7b3b..106af33b61d6fa5331602c027bc85190e6d7a291 100644 (file)
@@ -6,6 +6,8 @@
 """
 import sys
 
+from twisted.internet import defer
+
 import Xc; xc = Xc.new()
 import xenctl.ip
 
@@ -59,21 +61,26 @@ class XendDomain:
         for d in domlist:
             domid = str(d['dom'])
             doms[domid] = d
+        dlist = []
         for config in self.domain_db.values():
             domid = str(sxp.child_value(config, 'id'))
             print "dom=", domid, "config=", config
             if domid in doms:
                 print "dom=", domid, "new"
-                self._new_domain(config)
+                deferred = self._new_domain(config, doms[domid])
+                dlist.append(deferred)
             else:
                 print "dom=", domid, "del"
                 self._delete_domain(domid)
-        print "doms:"
-        for d in self.domain.values(): print 'dom', d
-        print "refresh..."
-        self.refresh()
-        print "doms:"
-        for d in self.domain.values(): print 'dom', d
+        deferred = defer.DeferredList(dlist, fireOnOneErrback=1)
+        def cbok(val):
+            print "doms:"
+            for d in self.domain.values(): print 'dom', d
+            print "refresh..."
+            self.refresh()
+            print "doms:"
+            for d in self.domain.values(): print 'dom', d
+        deferred.addCallback(cbok)
 
     def sync(self):
         """Sync domain db to disk.
@@ -90,31 +97,36 @@ class XendDomain:
     def close(self):
         pass
 
-    def _new_domain(self, info):
+    def _new_domain(self, savedinfo, info):
         """Create a domain entry from saved info.
         """
-        console = None
-        kernel = None
-        id = sxp.child_value(info, 'id')
-        dom = int(id)
-        name = sxp.child_value(info, 'name')
-        memory = int(sxp.child_value(info, 'memory'))
-        consoleinfo = sxp.child(info, 'console')
-        if consoleinfo:
-            consoleid = sxp.child_value(consoleinfo, 'id')
-            console = self.xconsole.console_get(consoleid)
-        if dom and console is None:
-            # Try to connect a console.
-            console = self.xconsole.console_create(dom)
-        config = sxp.child(info, 'config')
-        if config:
-            image = sxp.child(info, 'image')
-            if image:
-                image = sxp.child0(image)
-                kernel = sxp.child_value(image, 'kernel')
-        dominfo = XendDomainInfo.XendDomainInfo(
-            config, dom, name, memory, kernel, console)
-        self.domain[id] = dominfo
+##         console = None
+##         kernel = None
+##         id = sxp.child_value(info, 'id')
+##         dom = int(id)
+##         name = sxp.child_value(info, 'name')
+##         memory = int(sxp.child_value(info, 'memory'))
+##         consoleinfo = sxp.child(info, 'console')
+##         if consoleinfo:
+##             consoleid = sxp.child_value(consoleinfo, 'id')
+##             console = self.xconsole.console_get(consoleid)
+##         if dom and console is None:
+##             # Try to connect a console.
+##             console = self.xconsole.console_create(dom)
+##         config = sxp.child(info, 'config')
+##         if config:
+##             image = sxp.child(info, 'image')
+##             if image:
+##                 image = sxp.child0(image)
+##                 kernel = sxp.child_value(image, 'kernel')
+##         dominfo = XendDomainInfo.XendDomainInfo(
+##             config, dom, name, memory, kernel, console)
+        config = sxp.child_value(savedinfo, 'config')
+        deferred = XendDomainInfo.vm_recreate(config, info)
+        def fn(dominfo):
+            self.domain[dominfo.id] = dominfo
+        deferred.addCallback(fn)
+        return deferred
 
     def _add_domain(self, id, info, notify=1):
         self.domain[id] = info
@@ -143,10 +155,13 @@ class XendDomain:
             doms[id] = d
             if id not in self.domain:
                 config = None
-                image = None
-                newinfo = XendDomainInfo.XendDomainInfo(
-                    config, d['dom'], d['name'], d['mem_kb']/1024, image=image, info=d)
-                self._add_domain(newinfo.id, newinfo)
+                #image = None
+                #newinfo = XendDomainInfo.XendDomainInfo(
+                #    config, d['dom'], d['name'], d['mem_kb']/1024, image=image, info=d)
+                deferred = XendDomainInfo.vm_recreate(config, d)
+                def fn(dominfo):
+                    self._add_domain(dominfo.id, dominfo)
+                deferred.addCallback(fn)
         # Remove entries for domains that no longer exist.
         for d in self.domain.values():
             dominfo = doms.get(d.id)
@@ -217,13 +232,13 @@ class XendDomain:
         self.refresh()
         return val
     
-    def domain_halt(self, id):
+    def domain_destroy(self, id):
         """Terminate domain immediately.
         """
         dom = int(id)
         if dom <= 0:
             return 0
-        eserver.inject('xend.domain.halt', id)
+        eserver.inject('xend.domain.destroy', id)
         val = xc.domain_destroy(dom=dom)
         self.refresh()
         return val       
@@ -235,14 +250,14 @@ class XendDomain:
         pass
     
     def domain_save(self, id, dst, progress=0):
-        """Save domain state to file, halt domain.
+        """Save domain state to file, destroy domain.
         """
         dom = int(id)
         self.domain_pause(id)
         eserver.inject('xend.domain.save', id)
         rc = xc.linux_save(dom=dom, state_file=dst, progress=progress)
         if rc == 0:
-            self.domain_halt(id)
+            self.domain_destroy(id)
         return rc
     
     def domain_restore(self, src, config, progress=0):
index b263942c5b68d8c0e39478b0c8d3f9778046932f..0dfd1a71ee8986f35d682c4d5d19ad246cc0187e 100644 (file)
@@ -60,160 +60,6 @@ class VmError(ValueError):
         return self.value
 
 
-class XendDomainInfo:
-    """Virtual machine object."""
-
-    def __init__(self, config, dom, name, memory, image=None, console=None, info=None):
-        """Construct a virtual machine object.
-
-        config   configuration
-        dom      domain id
-        name     name
-        memory   memory size (in MB)
-        image    image object
-        """
-        #todo: add info: runtime, state, ...
-        self.config = config
-        self.id = str(dom)
-        self.dom = dom
-        self.name = name
-        self.memory = memory
-        self.image = image
-        self.console = console
-        self.devices = {}
-        self.configs = []
-        self.info = info
-        self.ipaddrs = []
-        self.block_controller = 0
-        self.net_controller = 0
-
-        #todo: state: running, suspended
-        self.state = 'running'
-        #todo: set to migrate info if migrating
-        self.migrate = None
-
-    def update(self, info):
-        """Update with  info from xc.domain_getinfo().
-        """
-        self.info = info
-
-    def __str__(self):
-        s = "domain"
-        s += " id=" + self.id
-        s += " name=" + self.name
-        s += " memory=" + str(self.memory)
-        if self.console:
-            s += " console=" + self.console.id
-        if self.image:
-            s += " image=" + self.image
-        s += ""
-        return s
-
-    __repr__ = __str__
-
-    def sxpr(self):
-        sxpr = ['domain',
-                ['id', self.id],
-                ['name', self.name],
-                ['memory', self.memory] ]
-        if self.info:
-            run   = (self.info['running'] and 'r') or '-'
-            block = (self.info['blocked'] and 'b') or '-'
-            stop  = (self.info['paused']  and 'p') or '-'
-            susp  = (self.info['shutdown'] and 's') or '-'
-            crash = (self.info['crashed'] and 'c') or '-'
-            state = run + block + stop + susp + crash
-            sxpr.append(['state', state])
-            if self.info['shutdown']:
-                reasons = ["poweroff", "reboot", "suspend"]
-                reason = reasons[self.info['shutdown_reason']]
-                sxpr.append(['shutdown_reason', reason])
-            sxpr.append(['cpu', self.info['cpu']])
-            sxpr.append(['cpu_time', self.info['cpu_time']/1e9])
-        if self.console:
-            sxpr.append(self.console.sxpr())
-        if self.config:
-            sxpr.append(['config', self.config])
-        return sxpr
-
-    def add_device(self, type, dev):
-        """Add a device to a virtual machine.
-
-        dev      device to add
-        """
-        dl = self.devices.get(type, [])
-        dl.append(dev)
-        self.devices[type] = dl
-
-    def get_devices(self, type):
-        val = self.devices.get(type, [])
-        print 'get_devices', type; sxp.show(val); print
-        return val
-
-    def get_device_by_id(self, type, id):
-        """Get the device with the given id.
-
-        id       device id
-
-        returns  device or None
-        """
-        return sxp.child_with_id(self.get_devices(type), id)
-
-    def get_device_by_index(self, type, idx):
-        """Get the device with the given index.
-
-        idx       device index
-
-        returns  device or None
-        """
-        dl = self.get_devices(type)
-        if 0 <= idx < len(dl):
-            return dl[idx]
-        else:
-            return None
-
-    def add_config(self, val):
-        """Add configuration data to a virtual machine.
-
-        val      data to add
-        """
-        self.configs.append(val)
-
-    def destroy(self):
-        if self.dom <= 0:
-            return 0
-        return xc.domain_destroy(dom=self.dom)
-
-    def died(self):
-        print 'died>', self.dom
-        self.release_vifs()
-
-    def release_vifs(self):
-        print 'release_vifs>', self.dom
-        vifs = self.get_devices('vif')
-        for v in vifs:
-            vif = sxp.child_value(v, 'vif')
-            bridge = sxp.child_value(v, 'bridge')
-            XendBridge.vif_bridge_rem(self.dom, vif, bridge)
-
-    def show(self):
-        """Print virtual machine info.
-        """
-        print "[VM dom=%d name=%s memory=%d" % (self.dom, self.name, self.memory)
-        print "image:"
-        sxp.show(self.image)
-        print
-        for dl in self.devices:
-            for dev in dl:
-                print "device:"
-                sxp.show(dev)
-                print
-        for val in self.configs:
-            print "config:"
-            sxp.show(val)
-            print
-        print "]"
-
 def blkdev_name_to_number(name):
     """Take the given textual block-device name (e.g., '/dev/sda1',
     'hda') and return the device number used by the OS. """
@@ -264,7 +110,7 @@ def lookup_raw_partn(partition):
     
     return None
 
-def lookup_disk_uname( uname ):
+def lookup_disk_uname(uname):
     """Lookup a list of segments for a physical device.
     uname [string]:  name of the device in the format \'phy:dev\' for a physical device
     returns [list of dicts]: list of extents that make up the named device
@@ -277,7 +123,7 @@ def lookup_disk_uname( uname ):
         segments = None
     return segments
 
-def make_disk(dom, uname, dev, mode):
+def make_disk(dom, uname, dev, mode, recreate=0):
     """Create a virtual disk device for a domain.
 
     @returns Deferred
@@ -289,21 +135,21 @@ def make_disk(dom, uname, dev, mode):
         raise VmError("vbd: Multi-segment vdisk: uname=%s" % uname)
     segment = segments[0]
     vdev = blkdev_name_to_number(dev)
-    ctrl = xend.blkif_create(dom)
+    ctrl = xend.blkif_create(dom, recreate=recreate)
     
     def fn(ctrl):
-        return xend.blkif_dev_create(dom, vdev, mode, segment)
+        return xend.blkif_dev_create(dom, vdev, mode, segment, recreate=recreate)
     ctrl.addCallback(fn)
     return ctrl
         
-def make_vif(dom, vif, vmac):
+def make_vif(dom, vif, vmac, recreate=0):
     """Create a virtual network device for a domain.
 
     
     @returns Deferred
     """
-    xend.netif_create(dom)
-    d = xend.netif_dev_create(dom, vif, vmac)
+    xend.netif_create(dom, recreate=recreate)
+    d = xend.netif_dev_create(dom, vif, vmac, recreate=recreate)
     return d
 
 def vif_up(iplist):
@@ -338,49 +184,6 @@ def vif_up(iplist):
     finally:
         if not nlb: set_ip_nonlocal_bind(0)
 
-def xen_domain_create(config, ostype, name, memory, kernel, ramdisk, cmdline, vifs_n):
-    """Create a domain. Builds the image but does not configure it.
-
-    config  configuration
-    ostype  OS type
-    name    domain name
-    memory  domain memory (MB)
-    kernel  kernel image
-    ramdisk kernel ramdisk
-    cmdline kernel commandline
-    vifs_n  number of network interfaces
-    returns vm
-    """
-    flags = 0
-    if not os.path.isfile(kernel):
-        raise VmError('Kernel image does not exist: %s' % kernel)
-    if ramdisk and not os.path.isfile(ramdisk):
-        raise VMError('Kernel ramdisk does not exist: %s' % ramdisk)
-
-    cpu = int(sxp.child_value(config, 'cpu', '-1'))
-    print 'xen_domain_create> create ', memory, name, cpu
-    dom = xc.domain_create(mem_kb= memory * 1024, name= name, cpu= cpu)
-    if dom <= 0:
-        raise VmError('Creating domain failed: name=%s memory=%d kernel=%s'
-                      % (name, memory, kernel))
-    console = xendConsole.console_create(dom)
-    buildfn = getattr(xc, '%s_build' % ostype)
-    
-    print 'xen_domain_create> build ', ostype, dom, kernel, cmdline, ramdisk
-    if len(cmdline) >= 256:
-        print 'Warning: kernel cmdline too long'
-    err = buildfn(dom            = dom,
-                  image          = kernel,
-                  control_evtchn = console.port2,
-                  cmdline        = cmdline,
-                  ramdisk        = ramdisk,
-                  flags          = flags)
-    if err != 0:
-        raise VmError('Building domain failed: type=%s dom=%d err=%d'
-                      % (ostype, dom, err))
-    vm = XendDomainInfo(config, dom, name, memory, kernel, console)
-    return vm
-
 config_handlers = {}
 
 def add_config_handler(name, h):
@@ -450,34 +253,27 @@ def vm_create(config):
     returns Deferred
     raises VmError for invalid configuration
     """
-    # todo - add support for scheduling params?
     print 'vm_create>'
-    vm = None
-    try:
-        name = sxp.child_value(config, 'name')
-        memory = int(sxp.child_value(config, 'memory', '128'))
-        image = sxp.child_value(config, 'image')
-        
-        image_name = sxp.name(image)
-        image_handler = get_image_handler(image_name)
-        if image_handler is None:
-            raise VmError('unknown image type: ' + image_name)
-        vm = image_handler(config, name, memory, image)
-        deferred = vm_configure(vm, config)
-    except StandardError, ex:
-        # Catch errors, cleanup and re-raise.
-        if vm:
-            vm.destroy()
-        raise
-    def cbok(x):
-        print 'vm_create> cbok', x
-        return x
-    deferred.addCallback(cbok)
-    print 'vm_create<'
-    return deferred
+    vm = XendDomainInfo()
+    return vm.construct(config)
+
+def vm_recreate(config, info):
+    """Create the VM object for an existing domain.
+    """
+    vm = XendDomainInfo()
+    vm.recreate = 1
+    vm.setdom(info['dom'])
+    vm.name = info['name']
+    vm.memory = info['mem_kb']/1024
+    if config:
+        d = vm.construct(config)
+    else:
+        d = defer.Deferred()
+        d.callback(vm)
+    return d
 
 def vm_restore(src, config, progress=0):
-    """Restore a VM.
+    """Restore a VM from a disk image.
 
     src      saved state to restore
     config   configuration
@@ -485,11 +281,14 @@ def vm_restore(src, config, progress=0):
     returns  deferred
     raises   VmError for invalid configuration
     """
+    vm = XendDomainInfo()
+    vm.config = config
     ostype = "linux" #todo set from config
     restorefn = getattr(xc, "%s_restore" % ostype)
     dom = restorefn(state_file=src, progress=progress)
-    if dom < 0: return dom
-    deferred = dom_configure(dom, config)
+    if dom < 0:
+        raise VMError('restore failed')
+    deferred = vm.dom_configure(dom)
     def vifs_cb(val, vm):
         vif_up(vm.ipaddrs)
     deferred.addCallback(vifs_cb, vm)
@@ -501,115 +300,25 @@ def dom_get(dom):
         return domlist[0]
     return None
     
-def dom_configure(dom, config):
-    """Configure a domain.
-
-    dom    domain id
-    config configuration
-    returns deferred
-    """
-    d = dom_get(dom)
-    if not d:
-        raise VMError("Domain not found: %d" % dom)
-    try:
-        name = d['name']
-        memory = d['memory']/1024
-        image = None
-        vm = VM(config, dom, name, memory, image)
-        deferred = vm_configure(vm, config)
-    except StandardError, ex:
-        if vm:
-            vm.destroy()
-        raise
-    return deferred
 
 def append_deferred(dlist, v):
     if isinstance(v, defer.Deferred):
         dlist.append(v)
 
-def vm_create_devices(vm, config):
-    """Create the devices for a vm.
-
-    vm         virtual machine
-    config     configuration
-
-    returns Deferred
-    raises VmError for invalid devices
-    """
-    print '>vm_create_devices'
-    dlist = []
-    devices = sxp.children(config, 'device')
-    index = {}
-    for d in devices:
-        dev = sxp.child0(d)
-        if dev is None:
-            raise VmError('invalid device')
-        dev_name = sxp.name(dev)
-        dev_index = index.get(dev_name, 0)
-        dev_handler = get_device_handler(dev_name)
-        if dev_handler is None:
-            raise VmError('unknown device type: ' + dev_name)
-        v = dev_handler(vm, dev, dev_index)
-        append_deferred(dlist, v)
-        index[dev_name] = dev_index + 1
-    deferred = defer.DeferredList(dlist, fireOnOneErrback=1)
-    print '<vm_create_devices'
-    return deferred
-
-def config_controllers(vm, config):
-    for c in sxp.children(config, 'controller'):
-        name = sxp.name(c)
-        if name == 'block':
-            vm.block_controller = 1
-            xend.blkif_set_control_domain(vm.dom)
-        elif name == 'net':
-            vm.net_controller = 1
-            xend.netif_set_control_domain(vm.dom)
-        else:
-            raise VmError('invalid controller type:' + str(name))
-    
-def vm_configure(vm, config):
-    """Configure a vm.
-
-    vm         virtual machine
-    config     configuration
-
-    returns Deferred - calls callback with vm
-    """
-    config_controllers(vm, config)
-    if vm.block_controller:
-        d = defer.Deferred()
-        d.callback(1)
-    else:
-        d = xend.blkif_create(vm.dom)
-    d.addCallback(_vm_configure1, vm, config)
-    return d
-
-def _vm_configure1(val, vm, config):
-    d = vm_create_devices(vm, config)
+def _vm_configure1(val, vm):
+    d = vm.create_devices()
     print '_vm_configure1> made devices...'
     def cbok(x):
         print '_vm_configure1> cbok', x
         return x
     d.addCallback(cbok)
-    d.addCallback(_vm_configure2, vm, config)
+    d.addCallback(_vm_configure2, vm)
     print '_vm_configure1<'
     return d
 
-def _vm_configure2(val, vm, config):
+def _vm_configure2(val, vm):
     print '>callback _vm_configure2...'
-    dlist = []
-    index = {}
-    for field in sxp.children(config):
-        field_name = sxp.name(field)
-        field_index = index.get(field_name, 0)
-        field_handler = get_config_handler(field_name)
-        # Ignore unknown fields. Warn?
-        if field_handler:
-            v = field_handler(vm, config, field, field_index)
-            append_deferred(dlist, v)
-        index[field_name] = field_index + 1
-    d = defer.DeferredList(dlist, fireOnOneErrback=1)
+    d = vm.configure_fields()
     def cbok(results):
         print '_vm_configure2> cbok', results
         return vm
@@ -622,22 +331,369 @@ def _vm_configure2(val, vm, config):
     print '<_vm_configure2'
     return d
 
-def config_devices(config, name):
-    """Get a list of the 'device' nodes of a given type from a config.
+class XendDomainInfo:
+    """Virtual machine object."""
 
-    config     configuration
-    name       device type
-    return list of device configs
-    """
-    devices = []
-    for d in sxp.children(config, 'device'):
-        dev = sxp.child0(d)
-        if dev is None: continue
-        if name == sxp.name(dev):
-            devices.append(dev)
-    return devices
+    def __init__(self):
+        self.recreate = 0
+        self.config = None
+        self.id = None
+        self.dom = None
+        self.name = None
+        self.memory = None
+        self.image = None
+        self.ramdisk = None
+        self.cmdline = None
+        self.console = None
+        self.devices = {}
+        self.configs = []
+        self.info = None
+        self.ipaddrs = []
+        self.blkif_backend = 0
+        self.netif_backend = 0
+        #todo: state: running, suspended
+        self.state = 'running'
+        #todo: set to migrate info if migrating
+        self.migrate = None
+
+    def setdom(self, dom):
+        self.dom = int(dom)
+        self.id = str(dom)
         
-def vm_image_linux(config, name, memory, image):
+    def update(self, info):
+        """Update with  info from xc.domain_getinfo().
+        """
+        self.info = info
+
+    def __str__(self):
+        s = "domain"
+        s += " id=" + self.id
+        s += " name=" + self.name
+        s += " memory=" + str(self.memory)
+        if self.console:
+            s += " console=" + self.console.id
+        if self.image:
+            s += " image=" + self.image
+        s += ""
+        return s
+
+    __repr__ = __str__
+
+    def sxpr(self):
+        sxpr = ['domain',
+                ['id', self.id],
+                ['name', self.name],
+                ['memory', self.memory] ]
+        if self.info:
+            run   = (self.info['running'] and 'r') or '-'
+            block = (self.info['blocked'] and 'b') or '-'
+            stop  = (self.info['paused']  and 'p') or '-'
+            susp  = (self.info['shutdown'] and 's') or '-'
+            crash = (self.info['crashed'] and 'c') or '-'
+            state = run + block + stop + susp + crash
+            sxpr.append(['state', state])
+            if self.info['shutdown']:
+                reasons = ["poweroff", "reboot", "suspend"]
+                reason = reasons[self.info['shutdown_reason']]
+                sxpr.append(['shutdown_reason', reason])
+            sxpr.append(['cpu', self.info['cpu']])
+            sxpr.append(['cpu_time', self.info['cpu_time']/1e9])
+        if self.console:
+            sxpr.append(self.console.sxpr())
+        if self.config:
+            sxpr.append(['config', self.config])
+        return sxpr
+
+    def construct(self, config):
+        # todo - add support for scheduling params?
+        self.config = config
+        try:
+            self.name = sxp.child_value(config, 'name')
+            self.memory = int(sxp.child_value(config, 'memory', '128'))
+            self.configure_backends()
+            image = sxp.child_value(config, 'image')
+            image_name = sxp.name(image)
+            image_handler = get_image_handler(image_name)
+            if image_handler is None:
+                raise VmError('unknown image type: ' + image_name)
+            image_handler(self, image)
+            deferred = self.configure()
+        except StandardError, ex:
+            # Catch errors, cleanup and re-raise.
+            self.destroy()
+            raise
+        def cbok(x):
+            print 'vm_create> cbok', x
+            return x
+        deferred.addCallback(cbok)
+        print 'vm_create<'
+        return deferred
+
+    def config_devices(self, name):
+        """Get a list of the 'device' nodes of a given type from the config.
+
+        name   device type
+        return list of device configs
+        """
+        devices = []
+        for d in sxp.children(self.config, 'device'):
+            dev = sxp.child0(d)
+            if dev is None: continue
+            if name == sxp.name(dev):
+                devices.append(dev)
+        return devices
+
+    def add_device(self, type, dev):
+        """Add a device to a virtual machine.
+
+        dev      device to add
+        """
+        dl = self.devices.get(type, [])
+        dl.append(dev)
+        self.devices[type] = dl
+
+    def get_devices(self, type):
+        val = self.devices.get(type, [])
+        return val
+
+    def get_device_by_id(self, type, id):
+        """Get the device with the given id.
+
+        id       device id
+
+        returns  device or None
+        """
+        dl = self.get_devices(type)
+        for d in dl:
+            if d.getprop('id') == id:
+                return d
+        return None
+
+    def get_device_by_index(self, type, idx):
+        """Get the device with the given index.
+
+        idx       device index
+
+        returns  device or None
+        """
+        dl = self.get_devices(type)
+        if 0 <= idx < len(dl):
+            return dl[idx]
+        else:
+            return None
+
+    def add_config(self, val):
+        """Add configuration data to a virtual machine.
+
+        val      data to add
+        """
+        self.configs.append(val)
+
+    def destroy(self):
+        if self.dom <= 0:
+            return 0
+        return xc.domain_destroy(dom=self.dom)
+
+    def died(self):
+        print 'died>', self.dom
+        self.release_devices()
+
+    def release_devices(self):
+        print 'release_devices>', self.dom
+        self.release_vifs()
+        self.release_vbds()
+        self.devices = {}
+
+    def release_vifs(self):
+        print 'release_vifs>', self.dom
+        if self.dom is None: return
+        ctrl = xend.netif_get(self.dom)
+        if ctrl:
+            ctrl.destroy()
+
+    def release_vbds(self):
+        print 'release_vbds>', self.dom
+        if self.dom is None: return
+        ctrl = xend.blkif_get(self.dom)
+        if ctrl:
+            ctrl.destroy()
+
+    def show(self):
+        """Print virtual machine info.
+        """
+        print "[VM dom=%d name=%s memory=%d" % (self.dom, self.name, self.memory)
+        print "image:"
+        sxp.show(self.image)
+        print
+        for dl in self.devices:
+            for dev in dl:
+                print "device:"
+                sxp.show(dev)
+                print
+        for val in self.configs:
+            print "config:"
+            sxp.show(val)
+            print
+        print "]"
+
+    def init_domain(self):
+        """Initialize the domain memory.
+        """
+        if self.recreate: return
+        memory = self.memory
+        name = self.name
+        cpu = int(sxp.child_value(self.config, 'cpu', '-1'))
+        print 'init_domain>', memory, name, cpu
+        dom = xc.domain_create(mem_kb= memory * 1024, name= name, cpu= cpu)
+        if dom <= 0:
+            raise VmError('Creating domain failed: name=%s memory=%d'
+                          % (name, memory))
+        self.setdom(dom)
+
+    def build_domain(self, ostype, kernel, ramdisk, cmdline, vifs_n):
+        """Build the domain boot image.
+        """
+        if self.recreate: return
+        if len(cmdline) >= 256:
+            print 'Warning: kernel cmdline too long'
+        dom = self.dom
+        buildfn = getattr(xc, '%s_build' % ostype)
+        print 'build_domain>', ostype, dom, kernel, cmdline, ramdisk
+        flags = 0
+        if self.netif_backend: flags |= SIF_NET_BE_DOMAIN
+        if self.blkif_backend: flags |= SIF_BLK_BE_DOMAIN
+        err = buildfn(dom            = dom,
+                      image          = kernel,
+                      control_evtchn = self.console.port2,
+                      cmdline        = cmdline,
+                      ramdisk        = ramdisk,
+                      flags          = flags)
+        if err != 0:
+            raise VmError('Building domain failed: type=%s dom=%d err=%d'
+                          % (ostype, dom, err))
+
+    def create_domain(self, ostype, kernel, ramdisk, cmdline, vifs_n):
+        """Create a domain. Builds the image but does not configure it.
+
+        ostype  OS type
+        kernel  kernel image
+        ramdisk kernel ramdisk
+        cmdline kernel commandline
+        vifs_n  number of network interfaces
+        """
+        print 'create_domain>', ostype, kernel
+        if not self.recreate:
+            if not os.path.isfile(kernel):
+                raise VmError('Kernel image does not exist: %s' % kernel)
+            if ramdisk and not os.path.isfile(ramdisk):
+                raise VMError('Kernel ramdisk does not exist: %s' % ramdisk)
+        print 'create-domain> init_domain...'
+        self.init_domain()
+        print 'create_domain>', 'dom=', self.dom
+        self.console = xendConsole.console_create(self.dom)
+        self.build_domain(ostype, kernel, ramdisk, cmdline, vifs_n)
+        self.image = kernel
+        self.ramdisk = ramdisk
+        self.cmdline = cmdline
+
+    def create_devices(self):
+        """Create the devices for a vm.
+
+        returns Deferred
+        raises VmError for invalid devices
+        """
+        print '>create_devices'
+        dlist = []
+        devices = sxp.children(self.config, 'device')
+        index = {}
+        for d in devices:
+            dev = sxp.child0(d)
+            if dev is None:
+                raise VmError('invalid device')
+            dev_name = sxp.name(dev)
+            dev_index = index.get(dev_name, 0)
+            dev_handler = get_device_handler(dev_name)
+            if dev_handler is None:
+                raise VmError('unknown device type: ' + dev_name)
+            v = dev_handler(self, dev, dev_index)
+            append_deferred(dlist, v)
+            index[dev_name] = dev_index + 1
+        deferred = defer.DeferredList(dlist, fireOnOneErrback=1)
+        print '<create_devices'
+        return deferred
+
+    def configure_backends(self):
+        """Set configuration flags if the vm is a backend for netif of blkif.
+        """
+        for c in sxp.children(self.config, 'backend'):
+            name = sxp.name(c)
+            if name == 'blkif':
+                self.blkif_backend = 1
+            elif name == 'netif':
+                self.netif_backend = 1
+            else:
+                raise VmError('invalid backend type:' + str(name))
+
+    def create_backends(self):
+        """Setup the netif and blkif backends.
+        """
+        if self.blkif_backend:
+            xend.blkif_set_control_domain(self.dom, recreate=self.recreate)
+        if self.netif_backend:
+            xend.netif_set_control_domain(self.dom, recreate=self.recreate)
+            
+    def configure(self):
+        """Configure a vm.
+
+        vm         virtual machine
+        config     configuration
+
+        returns Deferred - calls callback with vm
+        """
+        if self.blkif_backend:
+            d = defer.Deferred()
+            d.callback(1)
+        else:
+            d = xend.blkif_create(self.dom, recreate=self.recreate)
+        d.addCallback(_vm_configure1, self)
+        return d
+
+    def dom_configure(self, dom):
+        """Configure a domain.
+
+        dom    domain id
+        returns deferred
+        """
+        d = dom_get(dom)
+        if not d:
+            raise VMError("Domain not found: %d" % dom)
+        try:
+            self.setdom(dom)
+            self.name = d['name']
+            self.memory = d['memory']/1024
+            deferred = self.configure()
+        except StandardError, ex:
+            self.destroy()
+            raise
+        return deferred
+
+    def configure_fields(self):
+        dlist = []
+        index = {}
+        for field in sxp.children(self.config):
+            field_name = sxp.name(field)
+            field_index = index.get(field_name, 0)
+            field_handler = get_config_handler(field_name)
+            # Ignore unknown fields. Warn?
+            if field_handler:
+                v = field_handler(self, self.config, field, field_index)
+                append_deferred(dlist, v)
+            index[field_name] = field_index + 1
+        d = defer.DeferredList(dlist, fireOnOneErrback=1)
+        return d
+
+
+def vm_image_linux(vm, image):
     """Create a VM for a linux image.
 
     name      vm name
@@ -658,12 +714,11 @@ def vm_image_linux(config, name, memory, image):
     if args:
         cmdline += " " + args
     ramdisk = sxp.child_value(image, "ramdisk", '')
-    vifs = config_devices(config, "vif")
-    vm = xen_domain_create(config, "linux", name, memory, kernel,
-                           ramdisk, cmdline, len(vifs))
+    vifs = vm.config_devices("vif")
+    vm.create_domain("linux", kernel, ramdisk, cmdline, len(vifs))
     return vm
 
-def vm_image_netbsd(config, name, memory, image):
+def vm_image_netbsd(vm, image):
     """Create a VM for a bsd image.
 
     name      vm name
@@ -685,9 +740,8 @@ def vm_image_netbsd(config, name, memory, image):
     if args:
         cmdline += " " + args
     ramdisk = sxp.child_value(image, "ramdisk")
-    vifs = config_devices(config, "vif")
-    vm = xen_domain_create(config, "netbsd", name, memory, kernel,
-                           ramdisk, cmdline, len(vifs))
+    vifs = vm.config_devices("vif")
+    vm.create_domain("netbsd", kernel, ramdisk, cmdline, len(vifs))
     return vm
 
 
@@ -698,18 +752,18 @@ def vm_dev_vif(vm, val, index):
     val       vif config
     index     vif index
     """
-    if vm.net_controller:
-        raise VmError('vif: vif in control domain')
+    if vm.netif_backend:
+        raise VmError('vif: vif in netif backend domain')
     vif = index #todo
     vmac = sxp.child_value(val, "mac")
-    defer = make_vif(vm.dom, vif, vmac)
+    defer = make_vif(vm.dom, vif, vmac, vm.recreate)
     def fn(id):
+        dev = xend.netif_dev(vm.dom, vif)
+        devid = sxp.attribute(val, 'id')
+        if devid:
+            dev.setprop('id', devid)
         bridge = sxp.child_value(val, "bridge")
-        bridge = XendBridge.vif_bridge_add(vm.dom, vif, bridge)
-        dev = ['vif', ['vif', vif], ['bridge', bridge] ]
-        netdev = xend.netif_dev(vm.dom, vif)
-        if netdev and netdev.mac:
-            dev += [ 'mac', netdev.mac ]
+        dev.bridge_add(bridge)
         vm.add_device('vif', dev)
         print 'vm_dev_vif> created', dev
         return id
@@ -723,8 +777,9 @@ def vm_dev_vbd(vm, val, index):
     val       vbd config
     index     vbd index
     """
-    if vm.block_controller:
-        raise VmError('vbd: vbd in control domain')
+    if vm.blkif_backend:
+        raise VmError('vbd: vbd in blkif backend domain')
+    vdev = index
     uname = sxp.child_value(val, 'uname')
     if not uname:
         raise VMError('vbd: Missing uname')
@@ -732,9 +787,10 @@ def vm_dev_vbd(vm, val, index):
     if not dev:
         raise VMError('vbd: Missing dev')
     mode = sxp.child_value(val, 'mode', 'r')
-    defer = make_disk(vm.dom, uname, dev, mode)
+    defer = make_disk(vm.dom, uname, dev, mode, vm.recreate)
     def fn(vbd):
-        vm.add_device('vbd', val)
+        dev = xend.blkif_dev(vm.dom, vdev)
+        vm.add_device('vbd', dev)
         return vbd
     defer.addCallback(fn)
     return defer
@@ -765,7 +821,8 @@ def vm_dev_pci(vm, val, index):
         func = parse_pci(func)
     except:
         raise VMError('pci: invalid parameter')
-    rc = xc.physdev_pci_access_modify(dom=vm.dom, bus=bus, dev=dev, func=func, enable=1)
+    rc = xc.physdev_pci_access_modify(dom=vm.dom, bus=bus, dev=dev,
+                                      func=func, enable=1)
     if rc < 0:
         #todo non-fatal
         raise VMError('pci: Failed to configure device: bus=%s dev=%s func=%s' %
@@ -833,13 +890,11 @@ def vm_field_vnet(vm, config, val, index):
         if id is None:
             raise VmError('vnet: missing vif id')
         dev = vm.get_device_by_id('vif', id)
-        if not sxp.elementp(dev, 'vif'):
-            raise VmError('vnet: invalid vif id %s' % id)
-        vnet = sxp.child_value(v, 'vnet', 1)
-        mac = sxp.child_value(dev, 'mac')
-        vif = sxp.child_value(dev, 'vif')
-        vnet_bridge(vnet, mac, vm.dom, 0)
-        vm.add_config([ 'vif.vnet', ['id', id], ['vnet', vnet], ['mac', mac]])
+        #vnet = sxp.child_value(v, 'vnet', 1)
+        #mac = sxp.child_value(dev, 'mac')
+        #vif = sxp.child_value(dev, 'vif')
+        #vnet_bridge(vnet, mac, vm.dom, 0)
+        #vm.add_config([ 'vif.vnet', ['id', id], ['vnet', vnet], ['mac', mac]])
 
 # Register image handlers for linux and bsd.
 add_image_handler('linux',  vm_image_linux)
index 7179a0d1d47b0da43af27df9246e566cbee98f62..94b7c8e6c8aaa1962b14abbf2c58bd9a693248d6 100644 (file)
@@ -584,28 +584,31 @@ class Daemon:
         reactor.diconnectAll()
         sys.exit(0)
 
-    def blkif_set_control_domain(self, dom):
+    def blkif_set_control_domain(self, dom, recreate=0):
         """Set the block device backend control domain.
         """
-        return self.blkifCF.setControlDomain(dom)
+        return self.blkifCF.setControlDomain(dom, recreate=recreate)
     
     def blkif_get_control_domain(self, dom):
         """Get the block device backend control domain.
         """
         return self.blkifCF.getControlDomain()
     
-    def blkif_create(self, dom):
+    def blkif_create(self, dom, recreate=0):
         """Create a block device interface controller.
         
         Returns Deferred
         """
-        d = self.blkifCF.createInstance(dom)
+        d = self.blkifCF.createInstance(dom, recreate=recreate)
         return d
 
+    def blkif_get(self, dom):
+        return self.blkifCF.getInstanceByDom(dom)
+
     def blkif_dev(self, dom, vdev):
         return self.blkifCF.getDomainDevice(dom, vdev)
 
-    def blkif_dev_create(self, dom, vdev, mode, segment):
+    def blkif_dev_create(self, dom, vdev, mode, segment, recreate=0):
         """Create a block device.
         
         Returns Deferred
@@ -614,26 +617,29 @@ class Daemon:
         if not ctrl:
             raise ValueError('No blkif controller: %d' % dom)
         print 'blkif_dev_create>', dom, vdev, mode, segment
-        d = ctrl.attach_device(vdev, mode, segment)
+        d = ctrl.attachDevice(vdev, mode, segment, recreate=recreate)
         return d
 
-    def netif_set_control_domain(self, dom):
+    def netif_set_control_domain(self, dom, recreate=0):
         """Set the network interface backend control domain.
         """
-        return self.netifCF.setControlDomain(dom)
+        return self.netifCF.setControlDomain(dom, recreate=recreate)
 
     def netif_get_control_domain(self, dom):
         """Get the network interface backend control domain.
         """
         return self.netifCF.getControlDomain()
     
-    def netif_create(self, dom):
+    def netif_create(self, dom, recreate=0):
         """Create a network interface controller.
         
         """
-        return self.netifCF.createInstance(dom)
+        return self.netifCF.createInstance(dom, recreate=recreate)
+
+    def netif_get(self, dom):
+        return self.netifCF.getInstanceByDom(dom)
 
-    def netif_dev_create(self, dom, vif, vmac):
+    def netif_dev_create(self, dom, vif, vmac, recreate=0):
         """Create a network device.
 
         todo
@@ -641,7 +647,7 @@ class Daemon:
         ctrl = self.netifCF.getInstanceByDom(dom)
         if not ctrl:
             raise ValueError('No netif controller: %d' % dom)
-        d = ctrl.attach_device(vif, vmac)
+        d = ctrl.attachDevice(vif, vmac, recreate=recreate)
         return d
 
     def netif_dev(self, dom, vif):
index 5034869e63c489e1e9932bfbfaa13c8d588babb4..d8287e05ea8b30754f4fb6faa32585a34a9b27ab 100644 (file)
@@ -32,8 +32,8 @@ class SrvDomain(SrvDir):
         req.setHeader("Location", "%s/.." % req.prePathURL())
         return val
 
-    def op_halt(self, op, req):
-        val = self.xd.domain_halt(self.dom.id)
+    def op_destroy(self, op, req):
+        val = self.xd.domain_destroy(self.dom.id)
         req.setHeader("Location", "%s/.." % req.prePathURL())
         return val
 
@@ -196,7 +196,7 @@ class SrvDomain(SrvDir):
         req.write('<input type="submit" name="op" value="unpause">')
         req.write('<input type="submit" name="op" value="pause">')
         req.write('<input type="submit" name="op" value="shutdown">')
-        req.write('<input type="submit" name="op" value="halt">')
+        req.write('<input type="submit" name="op" value="destroy">')
         req.write('<br><input type="submit" name="op" value="migrate">')
         req.write('To: <input type="text" name="destination">')
         req.write('</form>')
index 8da827baa9d65ed9401cf66bee6f4b56bf8cef11..92706316b1890e9da5685db6d235283f68989683 100755 (executable)
@@ -1,3 +1,5 @@
+from twisted.internet import defer
+
 import channel
 import controller
 from messages import *
@@ -22,7 +24,7 @@ class BlkifControllerFactory(controller.ControllerFactory):
         self.attached = 1
         self.registerChannel()
 
-    def createInstance(self, dom):
+    def createInstance(self, dom, recreate=0):
         d = self.addDeferred()
         blkif = self.getInstanceByDom(dom)
         if blkif:
@@ -30,7 +32,10 @@ class BlkifControllerFactory(controller.ControllerFactory):
         else:
             blkif = BlkifController(self, dom)
             self.addInstance(blkif)
-            blkif.send_be_create()
+            if recreate:
+                self.callDeferred(blkif)
+            else:
+                blkif.send_be_create()
         return d
 
     def getDomainDevices(self, dom):
@@ -41,10 +46,11 @@ class BlkifControllerFactory(controller.ControllerFactory):
         blkif = self.getInstanceByDom(dom)
         return (blkif and blkif.getDevice(vdev)) or None
 
-    def setControlDomain(self, dom):
+    def setControlDomain(self, dom, recreate=0):
         if self.dom == dom: return
         self.deregisterChannel()
-        self.attached = 0
+        if not recreate:
+            self.attached = 0
         self.dom = dom
         self.registerChannel()
         #
@@ -55,6 +61,28 @@ class BlkifControllerFactory(controller.ControllerFactory):
     def getControlDomain(self):
         return self.dom
 
+    def reattachDevice(self, dom, vdev):
+        blkif = self.getInstanceByDom(dom)
+        if blkif:
+            blkif.reattachDevice(vdev)
+        self.attached = self.devicesAttached()
+        if self.attached:
+            self.reattached()
+
+    def devicesAttached(self):
+        """Check if all devices are attached.
+        """
+        attached = 1
+        for blkif in self.getInstances():
+            if not blkif.attached:
+                attached = 0
+                break
+        return attached
+                         
+    def reattached(self):
+        for blkif in self.getInstances():
+            blkif.reattached()
+
     def recv_be_create(self, msg, req):
         #print 'recv_be_create>'
         val = unpackMsg('blkif_be_create_t', msg)
@@ -86,29 +114,7 @@ class BlkifControllerFactory(controller.ControllerFactory):
         if self.attached:
             self.callDeferred(0)
         else:
-            self.reattach_device(val['domid'], val['vdevice'])
-
-    def reattach_device(self, dom, vdev):
-        blkif = self.getInstanceByDom(dom)
-        if blkif:
-            blkif.reattach_device(vdev)
-        self.attached = self.devices_attached()
-        if self.attached:
-            self.reattached()
-
-    def devices_attached(self):
-        """Check if all devices are attached.
-        """
-        attached = 1
-        for blkif in self.getInstances():
-            if not blkif.attached:
-                attached = 0
-                break
-        return attached
-                         
-    def reattached(self):
-        for blkif in self.getInstances():
-            blkif.reattached()
+            self.reattachDevice(val['domid'], val['vdevice'])
 
     def recv_be_driver_status_changed(self, msg, req):
         val = unpackMsg('blkif_be_driver_status_changed_t', msg)
@@ -117,11 +123,12 @@ class BlkifControllerFactory(controller.ControllerFactory):
             for blkif in self.getInstances():
                 blkif.detach()
 
-class BlkDev:
+class BlkDev(controller.Dev):
     """Info record for a block device.
     """
 
-    def __init__(self, vdev, mode, segment):
+    def __init__(self, ctrl, vdev, mode, segment):
+        controller.Dev.__init__(self, ctrl)
         self.vdev = vdev
         self.mode = mode
         self.device = segment['device']
@@ -131,6 +138,14 @@ class BlkDev:
 
     def readonly(self):
         return 'w' not in self.mode
+
+    def sxpr(self):
+        print 'BlkDev>sxpr>', vars(self)
+        val = ['blkif', ['vdev', self.vdev], ['mode', self.mode] ]
+        return val
+
+    def destroy(self):
+        self.controller.send_be_vbd_destroy(self.vdev)
         
 class BlkifController(controller.Controller):
     """Block device interface controller. Handles all block devices
@@ -154,21 +169,43 @@ class BlkifController(controller.Controller):
         self.registerChannel()
         #print 'BlkifController<', 'dom=', self.dom, 'idx=', self.idx
 
+    def lostChannel(self):
+        print 'BlkifController>lostChannel>', 'dom=', self.dom
+        #self.destroyDevices()
+        controller.Controller.lostChannel(self)
+
     def getDevices(self):
         return self.devices.values()
 
     def getDevice(self, vdev):
         return self.devices.get(vdev)
 
-    def attach_device(self, vdev, mode, segment):
+    def addDevice(self, vdev, mode, segment):
+        if vdev in self.devices: return None
+        dev = BlkDev(self, vdev, mode, segment)
+        self.devices[vdev] = dev
+        return dev
+
+    def attachDevice(self, vdev, mode, segment, recreate=0):
         """Attach a device to the specified interface.
         """
         #print 'BlkifController>attach_device>', self.dom, vdev, mode, segment
-        if vdev in self.devices: return -1
-        dev = BlkDev(vdev, mode, segment)
-        self.devices[vdev] = dev
-        self.send_be_vbd_create(vdev)
-        return self.factory.addDeferred()
+        dev = self.addDevice(vdev, mode, segment)
+        if not dev: return -1
+        if recreate:
+            d = defer.Deferred()
+            d.callback(self)
+        else:
+            self.send_be_vbd_create(vdev)
+            d = self.factory.addDeferred()
+        return d
+
+    def destroy(self):
+        self.destroyDevices()
+
+    def destroyDevices(self):
+        for dev in self.getDevices():
+            dev.destroy()
 
     def detach(self):
         """Detach all devices, when the back-end control domain has changed.
@@ -178,7 +215,7 @@ class BlkifController(controller.Controller):
             dev.attached = 0
             self.send_be_vbd_create(vdev)
 
-    def reattach_device(self, vdev):
+    def reattachDevice(self, vdev):
         """Reattach a device, when the back-end control domain has changed.
         """
         dev = self.devices[vdev]
@@ -254,4 +291,13 @@ class BlkifController(controller.Controller):
                         'extent.sector_start'  : dev.start_sector,
                         'extent.sector_length' : dev.nr_sectors })
         self.factory.writeRequest(msg)
+
+    def send_be_vbd_destroy(self, vdev):
+        dev = self.devices[vdev]
+        msg = packMsg('blkif_be_vbd_destroy_t',
+                      { 'domid'                : self.dom,
+                        'blkif_handle'         : 0,
+                        'vdevice'              : dev.vdev })
+        del self.devices[vdev]
+        self.factory.writeRequest(msg)
     
index 791e987511d4b25b3da971885b7d1bf5b8b00b69..cb543fa57cb901e8bc39b6a8457ed79dbbf80ff4 100755 (executable)
@@ -95,7 +95,7 @@ class ControllerFactory(CtrlMsgRcvr):
         if instance.idx in self.instances:
             del self.instances[instance.idx]
 
-    def createInstance(self, dom):
+    def createInstance(self, dom, recreate=0):
         raise NotImplementedError()
 
     def instanceClosed(self, instance):
@@ -136,3 +136,30 @@ class Controller(CtrlMsgRcvr):
 
     def lostChannel(self):
         self.factory.instanceClosed(self)
+
+class Dev:
+
+    def __init__(self, controller):
+        self.controller = controller
+        self.props = {}
+
+    def setprop(self, k, v):
+        self.props[k] = v
+
+    def getprop(self, k, v=None):
+        return self.props.get(k, v)
+
+    def hasprop(self, k):
+        return k in self.props
+
+    def delprop(self, k):
+        if k in self.props:
+            del self.props[k]
+
+    #def __repr__(self):
+    #    return str(self.sxpr())
+
+    def sxpr(self):
+        raise NotImplementedError()
+
+    
index 78bc24526f201675d74169516829e5c6aff014e1..03136702790068b33f2505be48b13cb061e998f4 100644 (file)
@@ -76,6 +76,9 @@ blkif_formats = {
     'blkif_be_vbd_grow_t':
     (CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_GROW),
 
+    'blkif_be_vbd_destroy_t':
+    (CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_DESTROY),
+
     'blkif_fe_interface_status_changed_t':
     (CMSG_BLKIF_FE, CMSG_BLKIF_FE_INTERFACE_STATUS_CHANGED),
 
index f3da86ba824691bc8f84a42123d319be9a2d78e7..d2a6a53860a4a723d209433b54192ac437b50bb1 100755 (executable)
@@ -1,5 +1,9 @@
 import random
 
+from twisted.internet import defer
+
+from xenmgr import XendBridge
+
 import channel
 import controller
 from messages import *
@@ -22,7 +26,7 @@ class NetifControllerFactory(controller.ControllerFactory):
         self.attached = 1
         self.registerChannel()
 
-    def createInstance(self, dom):
+    def createInstance(self, dom, recreate=0):
         """Create or find the network interface controller for a domain.
         """
         #print 'netif>createInstance> dom=', dom
@@ -40,12 +44,13 @@ class NetifControllerFactory(controller.ControllerFactory):
         netif = self.getInstanceByDom(dom)
         return (netif and netif.getDevice(vif)) or None
         
-    def setControlDomain(self, dom):
+    def setControlDomain(self, dom, recreate=0):
         """Set the 'back-end' device driver domain.
         """
         if self.dom == dom: return
         self.deregisterChannel()
-        self.attached = 0
+        if not recreate:
+            self.attached = 0
         self.dom = dom
         self.registerChannel()
         #
@@ -98,20 +103,38 @@ class NetifControllerFactory(controller.ControllerFactory):
 ##                 print "Done notifying guests"
 ##                 recovery = False
                 
-class NetDev:
+class NetDev(controller.Dev):
     """Info record for a network device.
     """
 
-    def __init__(self, vif, mac):
+    def __init__(self, ctrl, vif, mac):
+        controller.Dev.__init__(self, ctrl)
         self.vif = vif
         self.mac = mac
         self.evtchn = None
+        self.bridge = None
 
     def sxpr(self):
         vif = str(self.vif)
         mac = ':'.join(map(lambda x: "%x" % x, self.mac))
-        return ['netif', ['vif', vif], ['mac', mac]]
-    
+        val = ['netif', ['vif', vif], ['mac', mac]]
+        if self.bridge:
+            val += ['bridge', self.bridge]
+        return val
+
+    def bridge_add(self, bridge):
+        self.bridge = XendBridge.vif_bridge_add(self.controller.dom, self.vif, bridge)
+
+    def bridge_rem(self):
+        if not self.bridge: return
+        XendBridge.vif_bridge_rem(self.controller.dom, self.vif, self.bridge)
+        self.bridge = None
+
+    def destroy(self):
+        self.bridge_rem()
+        self.controller.send_be_destroy(self.vif)
+        
+
 class NetifController(controller.Controller):
     """Network interface controller. Handles all network devices for a domain.
     """
@@ -150,8 +173,7 @@ class NetifController(controller.Controller):
 
     def lostChannel(self):
         print 'NetifController>lostChannel>', 'dom=', self.dom
-        #for vif in self.devices:
-        #    self.send_be_destroy(vif)
+        #self.destroyDevices()
         controller.Controller.lostChannel(self)
 
     def getDevices(self):
@@ -167,11 +189,18 @@ class NetifController(controller.Controller):
             mac = [ int(x, 16) for x in vmac.split(':') ]
         if len(mac) != 6: raise ValueError("invalid mac")
         #print "attach_device>", "vif=", vif, "mac=", mac
-        dev = NetDev(vif, mac)
+        dev = NetDev(self, vif, mac)
         self.devices[vif] = dev
         return dev
 
-    def attach_device(self, vif, vmac):
+    def destroy(self):
+        self.destroyDevices()
+        
+    def destroyDevices(self):
+        for dev in self.getDevices():
+            dev.destroy()
+
+    def attachDevice(self, vif, vmac, recreate=0):
         """Attach a network device.
         If vmac is None a random mac address is assigned.
 
@@ -179,8 +208,12 @@ class NetifController(controller.Controller):
         @param vmac mac address (string)
         """
         self.addDevice(vif, vmac)
-        d = self.factory.addDeferred()
-        self.send_be_create(vif)
+        if recreate:
+            d = defer.Deferred()
+            d.callback(self)
+        else:
+            d = self.factory.addDeferred()
+            self.send_be_create(vif)
         return d
 
     def reattach_devices(self):
index 29b4c7b6dcb553835865567ffea8fe265c05ebc4..e3ccad20b7b012053245521fe0f481d1937a7de5 100644 (file)
@@ -1,3 +1,6 @@
+# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+"""Domain creation.
+"""
 import string
 import sys
 
@@ -13,7 +16,7 @@ Create a domain.
 """)
 
 gopts.opt('help', short='h',
-         fn=set_value, default=0,
+         fn=set_true, default=0,
          use="Print this help.")
 
 gopts.opt('quiet', short='q',
@@ -36,21 +39,19 @@ gopts.opt('load', short='L', val='FILE',
           fn=set_value, default=None,
           use='Domain saved state to load.')
 
-def set_var(opt, k, v):
-    opt.set(v)
-    for d in string.split(v, ';' ):
-        (k, v) = string.split(d, '=')
-        opt.opts.setvar(k, v)
-
 gopts.opt('define', short='D', val='VAR=VAL',
          fn=set_var, default=None,
-         use="""Set variables before loading defaults, e.g. '-D vmid=3;ip=1.2.3.4'
-         to set vmid and ip.""")
+         use="""Set a variable before loading defaults, e.g. '-D vmid=3'
+         to set vmid. May be repeated to set more thanone variable.""")
 
 gopts.opt('dryrun', short='n',
          fn=set_true, default=0,
          use="Dry run - print the config but don't create the domain.")
 
+gopts.opt('name', short='N', val='NAME',
+          fn=set_value, default=None,
+          use="Domain name.")
+
 gopts.opt('console', short='c',
          fn=set_true, default=0,
          use="Connect to console after domain is created.")
@@ -71,34 +72,48 @@ gopts.opt('memory', short='m', val='MEMORY',
          fn=set_value, default=128,
          use="Domain memory in MB.")
 
+gopts.opt('blkif',
+          fn=set_true, default=0,
+          use="Make the domain a block device backend.")
+
+gopts.opt('netif',
+          fn=set_true, default=0,
+          use="Make the domain a network interface backend.")
+
 gopts.opt('disk', short='d', val='phy:DEV,VDEV,MODE',
          fn=append_value, default=[],
          use="""Add a disk device to a domain. The physical device is DEV, which
-         is exported to the domain as VDEV. The disk is read-only if MODE is r,
-         read-write if mode is 'w'.""")
+         is exported to the domain as VDEV. The disk is read-only if MODE
+         is 'r', read-write if MODE is 'w'.
+         The option may be repeated to add more than one disk.
+         """)
 
 gopts.opt('pci', val='BUS,DEV,FUNC',
          fn=append_value, default=[],
-         use="""Add a PCI device to a domain, using given params (in hex).""")
+         use="""Add a PCI device to a domain, using given params (in hex).
+         For example '-pci c0,02,1a'.
+         The option may be repeated to add more than one pci device.
+         """)
 
 gopts.opt('ipaddr', short='i', val="IPADDR",
          fn=append_value, default=[],
          use="Add an IP address to the domain.")
 
-gopts.opt('mac', short='M', val="MAC",
+gopts.opt('vif', val="mac=MAC,bridge=BRIDGE",
          fn=append_value, default=[],
-         use="""Add a network interface with the given mac address to the domain.
-         More than one interface may be specified. Interfaces with unspecified MAC addresses
-         are allocated a random address.""")
+         use="""Add a network interface with the given MAC address and bridge.
+         If mac is not specified a random MAC address is used.
+         If bridge is not specified the default bridge is used.
+         This option may be repeated to add more than one vif.
+         Specifying vifs will increase the number of interfaces as needed.
+         """)
 
-gopts.opt('nics', val="N",
+gopts.opt('nics', val="NUM",
          fn=set_int, default=1,
-         use="Set the number of network interfaces.")
-
-gopts.opt('vnet', val='VNET',
-         fn=append_value, default=[],
-         use="""Define the vnets for the network interfaces.
-         More than one vnet may be given, they are used in order.
+         use="""Set the number of network interfaces.
+         Use the vif option to define interface parameters, otherwise
+         defaults are used. Specifying vifs will increase the
+         number of interfaces as needed.
          """)
 
 gopts.opt('root', short='R', val='DEVICE',
@@ -116,15 +131,15 @@ gopts.opt('ip', short='I', val='IPADDR',
 
 gopts.opt('gateway', val="IPADDR",
          fn=set_value, default='',
-         use="Set kernel IP gateway.")
+         use="Set the kernel IP gateway.")
 
 gopts.opt('netmask', val="MASK",
          fn=set_value, default = '',
-         use="Set kernel IP netmask.")
+         use="Set the kernel IP netmask.")
 
 gopts.opt('hostname', val="NAME",
          fn=set_value, default='',
-         use="Set kernel IP hostname.")
+         use="Set the kernel IP hostname.")
 
 gopts.opt('interface', val="INTF",
          fn=set_value, default="eth0",
@@ -132,7 +147,7 @@ gopts.opt('interface', val="INTF",
 
 gopts.opt('dhcp', val="off|dhcp",
          fn=set_value, default='off',
-         use="Set kernel dhcp option.")
+         use="Set the kernel dhcp option.")
 
 gopts.opt('nfs_server', val="IPADDR",
          fn=set_value, default=None,
@@ -143,19 +158,16 @@ gopts.opt('nfs_root', val="PATH",
          use="Set the path of the root NFS directory.")
 
 def strip(pre, s):
+    """Strip prefix 'pre' if present.
+    """
     if s.startswith(pre):
         return s[len(pre):]
     else:
         return s
 
-def make_config(opts):
-    
-    config = ['config',
-              ['name', opts.name ],
-              ['memory', opts.memory ] ]
-    if opts.cpu:
-        config.append(['cpu', opts.cpu])
-    
+def configure_image(config, opts):
+    """Create the image config.
+    """
     config_image = [ opts.builder ]
     config_image.append([ 'kernel', os.path.abspath(opts.kernel) ])
     if opts.ramdisk:
@@ -169,8 +181,10 @@ def make_config(opts):
     if opts.extra:
         config_image.append(['args', opts.extra])
     config.append(['image', config_image ])
-       
-    config_devs = []
+    
+def configure_disks(config_devs, opts):
+    """Create the config for disks (virtual block devices).
+    """
     for (uname, dev, mode) in opts.disk:
         config_vbd = ['vbd',
                       ['uname', uname],
@@ -178,18 +192,34 @@ def make_config(opts):
                       ['mode', mode ] ]
         config_devs.append(['device', config_vbd])
 
+def configure_pci(config_devs, opts):
+    """Create the config for pci devices.
+    """
     for (bus, dev, func) in opts.pci:
         config_pci = ['pci', ['bus', bus], ['dev', dev], ['func', func]]
         config_devs.append(['device', config_pci])
 
-    for idx in range(0, opts.nics):
-        config_vif = ['vif', ['@', ['id', 'vif%d' % idx]]]
-        if idx < len(opts.mac):
-            config_vif.append(['mac', opts.mac[idx]])
+def configure_vifs(config_devs, opts):
+    """Create the config for virtual network interfaces.
+    """
+    vifs = opts.vif
+    vifs_n = max(opts.nics, len(vifs))
+
+    for idx in range(0, vifs_n):
+        if idx < len(vifs):
+            d = vifs[idx]
+            mac = d.get('mac')
+            bridge = d.get('bridge')
+        else:
+            mac = None
+            bridge = None
+        config_vif = ['vif']
+        if mac:
+            config_vif.append(['mac', mac])
+        if bridge:
+            config_vif.append(['bridge', bridge])
         config_devs.append(['device', config_vif])
 
-    config += config_devs
-
 ##     if vfr_ipaddr:
 ##         config_vfr = ['vfr']
 ##         idx = 0 # No way of saying which IP is for which vif?
@@ -197,15 +227,27 @@ def make_config(opts):
 ##             config_vfr.append(['vif', ['id', idx], ['ip', ip]])
 ##         config.append(config_vfr)
 
-    if opts.vnet:
-        config_vnet = ['vnet']
-        idx = 0
-        for vnet in opts.vnet:
-            config_vif = ['vif', ['id', 'vif%d' % idx], ['vnet', vnet]]
-            config_vnet.append(config_vif)
-            idx += 1
-        config.append(config_vnet)
-            
+
+def make_config(opts):
+    """Create the domain configuration.
+    """
+    
+    config = ['vm',
+              ['name', opts.name ],
+              ['memory', opts.memory ] ]
+    if opts.cpu:
+        config.append(['cpu', opts.cpu])
+    if opts.blkif:
+        config.append(['backend', ['blkif']])
+    if opts.netif:
+        config.append(['backend', ['netif']])
+    
+    configure_image(config, opts)
+    config_devs = []
+    configure_disks(config_devs, opts)
+    configure_pci(config_devs, opts)
+    configure_vifs(config_devs, opts)
+    config += config_devs
     return config
 
 def preprocess_disk(opts):
@@ -213,7 +255,6 @@ def preprocess_disk(opts):
     disk = []
     for v in opts.disk:
         d = v.split(',')
-        print 'disk', v, d
         if len(d) != 3:
             opts.err('Invalid disk specifier: ' + v)
         disk.append(d)
@@ -231,6 +272,22 @@ def preprocess_pci(opts):
         pci.append(hexd)
     opts.pci = pci
 
+def preprocess_vifs(opts):
+    if not opts.vif: return
+    vifs = []
+    for vif in opts.vif:
+        d = {}
+        a = vif.split(',')
+        for b in a:
+            (k, v) = b.strip().split('=')
+            k = k.strip()
+            v = v.strip()
+            if k not in ['mac', 'bridge']:
+                opts.err('Invalid vif specifier: ' + vif)
+            d[k] = v
+        vifs.append(d)
+    opts.vif = vifs
+
 def preprocess_ip(opts):
     setip = (opts.hostname or opts.netmask
              or opts.gateway or opts.dhcp or opts.interface)
@@ -259,6 +316,7 @@ def preprocess(opts):
         opts.err("No kernel specified")
     preprocess_disk(opts)
     preprocess_pci(opts)
+    preprocess_vifs(opts)
     preprocess_ip(opts)
     preprocess_nfs(opts)
          
@@ -266,8 +324,8 @@ def make_domain(opts, config):
     """Create, build and start a domain.
     Returns: [int] the ID of the new domain.
     """
-    if opts.load:
-        filename = os.path.abspath(opts.load)
+    if opts.vals.load:
+        filename = os.path.abspath(opts.vals.load)
         dominfo = server.xend_domain_restore(filename, config)
     else:
         dominfo = server.xend_domain_create(config)
@@ -280,7 +338,7 @@ def make_domain(opts, config):
         console_port = None
     
     if server.xend_domain_unpause(dom) < 0:
-        server.xend_domain_halt(dom)
+        server.xend_domain_destroy(dom)
         opts.err("Failed to start domain %d" % dom)
     opts.info("Started domain %d, console on port %d"
               % (dom, console_port))
@@ -289,16 +347,16 @@ def make_domain(opts, config):
 def main(argv):
     opts = gopts
     args = opts.parse(argv)
-    if opts.help:
+    if opts.vals.help:
         opts.usage()
         return
-    if opts.config:
+    if opts.vals.config:
         pass
     else:
         opts.load_defaults()
-    preprocess(opts)
-    config = make_config(opts)
-    if opts.dryrun:
+    preprocess(opts.vals)
+    config = make_config(opts.vals)
+    if opts.vals.dryrun:
         PrettyPrint.prettyprint(config)
     else:
         make_domain(opts, config)
index f124d63518cf95ee963348dddc961ee2a84bb6c0..86f606fd83903ebf34106466cccb417229efcd5c 100644 (file)
@@ -10,27 +10,72 @@ from xenmgr import sxp
 from xenmgr.XendClient import server
 from xenmgr.xm import create, shutdown
 
+class Prog:
+    """Base class for sub-programs.
+    """
+
+    """Program group it belongs to"""
+    group = 'all'
+    """Program name."""
+    name = '??'
+    """Short program info."""
+    info = ''
+
+    def __init__(self, xm):
+        self.xm = xm
+
+    def err(self, msg):
+        self.xm.err(msg)
+
+    def help(self, args):
+        self.shortHelp(args)
+
+    def shortHelp(self, args):
+        print "%-14s %s" % (self.name, self.info)
+
+    def main(self, args):
+        """Program main entry point.
+        """
+        pass
+
+
+class ProgUnknown(Prog):
+
+    name = 'unknown'
+    info = ''
+    
+    def help(self, args):
+        self.xm.err("Unknown command: %s\nTry '%s help' for more information."
+                    % (args[0], self.xm.name))
+
+    main = help
+
 class Xm:
+    """Main application.
+    """
 
     def __init__(self):
-        self.prog = 'xm'
-        pass
+        self.name = 'xm'
+        self.unknown = ProgUnknown(self)
+        self.progs = {}
 
     def err(self, msg):
         print >>sys.stderr, "Error:", msg
         sys.exit(1)
 
     def main(self, args):
-        """Main entry point. Dispatches to the xm_ methods.
+        """Main entry point. Dispatches to the progs.
         """
-        self.prog = args[0]
+        self.name = args[0]
         if len(args) < 2:
             self.err("Missing command\nTry '%s help' for more information."
-                     % self.prog)
-        prog = 'xm_' + args[1]
+                     % self.name)
         help = self.helparg(args)
-        fn = getattr(self, prog, self.unknown)
-        fn(help, args[1:])
+        p = self.getprog(args[1], self.unknown)
+        if help:
+            p.help(args[1:])
+        else:
+            p.main(args[1:])
 
     def helparg(self, args):
         for a in args:
@@ -38,39 +83,87 @@ class Xm:
                 return 1
         return 0
 
-    def unknown(self, help, args):
-        if help and len(args) == 1:
-            self.xm_help(help, args)
-        else:
-            self.err("Unknown command: %s\nTry '%s help' for more information."
-                     % (args[0], self.prog))
+    def prog(self, pklass):
+        """Add a sub-program.
+
+        pklass  program class (Prog subclass)
+        """
+        p = pklass(self)
+        self.progs[p.name] = p
+        return p
+
+    def getprog(self, name, val=None):
+        """Get a sub-program.
+        """
+        return self.progs.get(name, val)
 
-    def help(self, meth, args):
-        """Print help on an xm_ method.
-        Uses the method documentation string if there is one.
+    def proglist(self):
+        """Get a list of sub-programs, ordered by group.
         """
-        name = meth[3:]
-        f = getattr(self, meth)
-        print "%-14s %s" % (name, f.__doc__ or '')
-
-    def xm_help(self, help, args):
-        """Print help."""
-        for k in dir(self):
-            if not k.startswith('xm_'): continue
-            self.help(k, args)
-        print "\nTry '%s CMD -h' for help on CMD" % self.prog
-                
-    def xm_create(self, help, args):
-        """Create a domain."""
+        groups = {}
+        for p in self.progs.values():
+            l = groups.get(p.group, [])
+            l.append(p)
+            groups[p.group] = l
+        kl = groups.keys()
+        kl.sort()
+        pl = []
+        for k in kl:
+            l = groups[k]
+            l.sort()
+            pl += l
+        return pl
+        
+# Create the application object, then add the sub-program classes.
+xm = Xm()
+
+class ProgHelp(Prog):
+
+    name = "help"
+    info = "Print help."
+    
+    def help(self, args):
+        if len(args) == 2:
+            name = args[1]
+            p = self.xm.getprog(name)
+            if p:
+                p.help(args)
+            else:
+                print '%s: Unknown command: %s' % (self.name, name)
+        else:
+            for p in self.xm.proglist():
+                p.shortHelp(args)
+            print "\nTry '%s help CMD' for help on CMD" % self.xm.name
+
+    main = help
+
+xm.prog(ProgHelp)
+
+class ProgCreate(Prog):
+
+    group = 'domain'
+    name = "create"
+    info = """Create a domain."""
+
+    def help(self, args):
+        create.main([args[0], '-h'])
+
+    def main(self, args):
         create.main(args)
 
-    def xm_save(self, help, args):
-        """Save domain state (and config) to file."""
-        if help:
-            print args[0], "DOM FILE [CONFIG]"
-            print """\nSave domain with id DOM to FILE.
-            Optionally save config to CONFIG."""
-            return
+xm.prog(ProgCreate)
+
+class ProgSave(Prog):
+    group = 'domain'
+    name = "save"
+    info = """Save domain state (and config) to file."""
+
+    def help(self, args):
+        print args[0], "DOM FILE [CONFIG]"
+        print """\nSave domain with id DOM to FILE.
+        Optionally save config to CONFIG."""
+        
+    def main(self, args):
         if len(args) < 3: self.err("%s: Missing arguments" % args[0])
         dom = args[1]
         savefile = os.path.abspath(args[2])
@@ -83,23 +176,45 @@ class Xm:
             PrettyPrint.prettyprint(config, out=out)
             out.close()
         server.xend_domain_save(dom, savefile)
-            
-    def xm_restore(self, help, args):
-        """Create a domain from a saved state."""
-        if help:
-            print args[0], "FILE CONFIG"
-            print "\nRestore a domain from FILE using configuration CONFIG."
-            return
+
+xm.prog(ProgSave)
+
+class ProgRestore(Prog):
+    group = 'domain'
+    name = "restore"
+    info = """Create a domain from a saved state."""
+
+    def help(self, args):
+        print args[0], "FILE CONFIG"
+        print "\nRestore a domain from FILE using configuration CONFIG."
+    
+    def main(self, help, args):
         if len(args) < 3: self.err("%s: Missing arguments" % args[0])
         savefile =  os.path.abspath(args[1])
         configfile = os.path.abspath(args[2])
         info = server.xend_domain_restore(savefile, configfile)
         PrettyPrint.prettyprint(info)
 
-    def xm_domains(self, help, args):
-        """List domains."""
-        if help: self.help('xm_' + args[0], args); return
-        doms = server.xend_domains()
+xm.prog(ProgRestore)
+
+class ProgList(Prog):
+    group = 'domain'
+    name = "list"
+    info = """List info about domains."""
+
+    def help(self, args):
+        if help:
+            print args[0], '[DOM...]'
+            print """\nGet information about domains.
+            Either all domains or the domains given."""
+            return
+        
+    def main(self, args):
+        n = len(args)
+        if n == 1:
+            doms = server.xend_domains()
+        else:
+            doms = map(int, args[1:])
         doms.sort()
         print 'Dom  Name             Mem(MB)  CPU  State  Time(s)'
         for dom in doms:
@@ -113,111 +228,167 @@ class Xm:
             d['cpu_time'] = float(sxp.child_value(info, 'cpu_time', '0'))
             print ("%(dom)-4d %(name)-16s %(mem)7d  %(cpu)3d  %(state)5s  %(cpu_time)7.1f" % d)
 
-    def xm_domain(self, help, args):
-        """Get information about a domain."""
-        if help:
-            print args[0], 'DOM'
-            print '\nGet information about domain DOM.'
-            return
-        if len(args) < 2: self.err("%s: Missing domain" % args[0])
-        dom = args[1]
-        info = server.xend_domain(dom)
-        PrettyPrint.prettyprint(info)
-        print
+xm.prog(ProgList)
 
-    def xm_halt(self, help, args):
-        """Terminate a domain immediately."""
-        if help:
-            print args[0], 'DOM'
-            print '\nTerminate domain DOM immediately.'
-            return
+class ProgDestroy(Prog):
+    group = 'domain'
+    name = "destroy"
+    info = """Terminate a domain immediately."""
+
+    def help(self, args):
+        print args[0], 'DOM'
+        print '\nTerminate domain DOM immediately.'
+
+    def main(self, args):
         if len(args) < 2: self.err("%s: Missing domain" % args[0])
         dom = args[1]
-        server.xend_domain_halt(dom)
+        server.xend_domain_destroy(dom)
+
+xm.prog(ProgDestroy)
+
+class ProgShutdown(Prog):
+    group = 'domain'
+    name = "shutdown"
+    info = """Shutdown a domain."""
 
-    def xm_shutdown(self, help, args):
-        """Shutdown a domain."""
+    def help(self, args):
+        print args[0], 'DOM'
+        print '\nSignal domain DOM to shutdown.'
+    
+    def main(self, args):
         shutdown.main(args)
 
-    def xm_pause(self, help, args):
-        """Pause execution of a domain."""
-        if help:
-            print args[0], 'DOM'
-            print '\nPause execution of domain DOM.'
-            return
+xm.prog(ProgShutdown)
+
+class ProgPause(Prog):
+    group = 'domain'
+    name = "pause"
+    info = """Pause execution of a domain."""
+
+    def help(self, args):
+        print args[0], 'DOM'
+        print '\nPause execution of domain DOM.'
+
+    def main(self, args):
         if len(args) < 2: self.err("%s: Missing domain" % args[0])
         dom = args[1]
         server.xend_domain_pause(dom)
 
-    def xm_unpause(self, help, args):
-        """Unpause a paused domain."""
-        if help:
-            print args[0], 'DOM'
-            print '\nUnpause execution of domain DOM.'
-            return
+xm.prog(ProgPause)
+
+class ProgUnpause(Prog):
+    group = 'domain'
+    name = "unpause"
+    info = """Unpause a paused domain."""
+
+    def help(self, args):
+        print args[0], 'DOM'
+        print '\nUnpause execution of domain DOM.'
+
+    def main(self, args):
         if len(args) < 2: self.err("%s: Missing domain" % args[0])
         dom = args[1]
         server.xend_domain_unpause(dom)
 
-    def xm_pincpu(self, help, args):
-        """Pin a domain to a cpu. """
-        if help:
-            print args[0],'DOM CPU'
-            print '\nPin domain DOM to cpu CPU.'
-            return
+xm.prog(ProgUnpause)
+
+class ProgPincpu(Prog):
+    group = 'domain'
+    name = "pincpu"
+    info = """Pin a domain to a cpu. """
+
+    def help(self, args):
+        print args[0],'DOM CPU'
+        print '\nPin domain DOM to cpu CPU.'
+
+    def main(self, args):
         if len(args) != 3: self.err("%s: Invalid argument(s)" % args[0])
         v = map(int, args[1:3])
         server.xend_domain_pincpu(*v)
 
-    def xm_bvt(self, help, args):
-        """Set BVT scheduler parameters."""
-        if help:
-            print args[0], "DOM MCUADV WARP WARPL WARPU"
-            print '\nSet Borrowed Virtual Time scheduler parameters.'
-            return
+xm.prog(ProgPincpu)
+
+class ProgBvt(Prog):
+    group = 'scheduler'
+    name = "bvt"
+    info = """Set BVT scheduler parameters."""
+    
+    def help(self, args):
+        print args[0], "DOM MCUADV WARP WARPL WARPU"
+        print '\nSet Borrowed Virtual Time scheduler parameters.'
+
+    def main(self, args):
         if len(args) != 6: self.err("%s: Invalid argument(s)" % args[0])
         v = map(int, args[1:6])
         server.xend_domain_cpu_bvt_set(*v)
 
-    def xm_bvtslice(self, help, args):
-        """Set the BVT scheduler slice."""
-        if help:
-            print args[0], 'SLICE'
-            print '\nSet Borrowed Virtual Time scheduler slice.'
-            return
+xm.prog(ProgBvt)
+
+class ProgBvtslice(Prog):
+    group = 'scheduler'
+    name = "bvtslice"
+    info = """Set the BVT scheduler slice."""
+
+    def help(self, args):
+        print args[0], 'SLICE'
+        print '\nSet Borrowed Virtual Time scheduler slice.'
+
+    def main(self, args):
         if len(args) < 2: self.err('%s: Missing slice' % args[0])
         server.xend_node_cpu_bvt_slice_set(slice)
 
-    def xm_atropos(self, help, args):
-        """Set atropos parameters."""
-        if help:
-            print args[0], "DOM PERIOD SLICE LATENCY XTRATIME"
-            print "\nSet atropos parameters."
-            return
+xm.prog(ProgBvtslice)
+
+class ProgAtropos(Prog):
+    group = 'scheduler'
+    name= "atropos"
+    info = """Set atropos parameters."""
+
+    def help(self, args):
+        print args[0], "DOM PERIOD SLICE LATENCY XTRATIME"
+        print "\nSet atropos parameters."
+
+    def main(self, args):
         if len(args) != 5: self.err("%s: Invalid argument(s)" % args[0])
         v = map(int, args[1:5])
         server.xend_domain_cpu_atropos_set(*v)
 
-    def xm_rrobin(self, help, args):
-        """Set round robin slice."""
-        if help:
-            print args[0], "SLICE"
-            print "\nSet round robin scheduler slice."
-            return
+xm.prog(ProgAtropos)
+
+class ProgRrobin(Prog):
+    group = 'scheduler'
+    name = "rrobin"
+    info = """Set round robin slice."""
+
+    def help(self, args):
+        print args[0], "SLICE"
+        print "\nSet round robin scheduler slice."
+
+    def main(self, args):
         if len(args) != 2: self.err("%s: Invalid argument(s)" % args[0])
         rrslice = int(args[1])
         server.xend_node_rrobin_set(rrslice)
 
-    def xm_info(self, help, args):
-        """Get information about the xen host."""
-        if help: self.help('xm_' + args[0], args); return
+xm.prog(ProgRrobin)
+
+class ProgInfo(Prog):
+    group = 'host'
+    name = "info"
+    info = """Get information about the xen host."""
+
+    def main(self, args):
         info = server.xend_node()
         for x in info[1:]:
             print "%-23s:" % x[0], x[1]
 
-    def xm_consoles(self, help, args):
-        """Get information about domain consoles."""
-        if help: self.help('xm_' + args[0], args); return
+xm.prog(ProgInfo)
+
+class ProgConsoles(Prog):
+    group = 'console'
+    name = "consoles"
+    info = """Get information about domain consoles."""
+
+    def main(self, args):
         l = server.xend_consoles()
         print "Dom Port  Id"
         for x in l:
@@ -228,12 +399,18 @@ class Xm:
             d['id'] = sxp.child_value(info, 'id', '?')
             print "%(dom)3s %(port)4s %(id)3s" % d
 
-    def xm_console(self, help, args):
-        """Open a console to a domain."""
-        if help:
-            print "console DOM"
-            print "\nOpen a console to domain DOM."
-            return
+xm.prog(ProgConsoles)
+
+class ProgConsole(Prog):
+    group = 'console'
+    name = "console"
+    info = """Open a console to a domain."""
+    
+    def help(self, args):
+        print "console DOM"
+        print "\nOpen a console to domain DOM."
+
+    def main(self, args):
         if len(args) < 2: self.err("%s: Missing domain" % args[0])
         dom = args[1]
         info = server.xend_domain(dom)
@@ -244,6 +421,7 @@ class Xm:
         from xenctl import console_client
         console_client.connect("localhost", int(port))
 
+xm.prog(ProgConsole)
+
 def main(args):
-    xm = Xm()
     xm.main(args)
index 426a6d24d1604feaab7a4af058327323ec728a89..b2cb2c7463398640fc226222c7b74776c0572c83 100644 (file)
@@ -1,3 +1,6 @@
+# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+"""Object-oriented command-line option support.
+"""
 from getopt import getopt
 import os
 import os.path
@@ -5,8 +8,22 @@ import sys
 import types
 
 class Opt:
+    """An individual option.
+    """
     def __init__(self, opts, name, short=None, long=None,
                  val=None, fn=None, use=None, default=None):
+        """Create an option.
+
+        opts    parent options object
+        name    name of the field it controls
+        short   short (1-char) command line switch (optional)
+        long    long command-line switch. Defaults to option name.
+        val     string used to print option args in help.
+                If val is not specified the option has no arg.
+        fn      function to call when the option is specified.
+        use     usage (help) string
+        default default value if not specified on command-line
+        """
         self.opts = opts
         self.name = name
         self.short = short
@@ -24,18 +41,34 @@ class Opt:
         self.fn = fn
         self.specified_opt = None
         self.specified_val = None
+        self.value = None
         self.set(default)
 
+    def __repr__(self):
+        return self.name + '=' + str(self.specified_val)
+
+    __str__ = __repr__
+
     def set(self, value):
-        setattr(self.opts, self.name, value)
+        """Set the option value.
+        """
+        self.opts.setopt(self.name, value)
 
     def get(self):
-        return getattr(self.opts, self.name)
+        """Get the option value.
+        """
+        return self.opts.getopt(self.name)
 
     def append(self, value):
-        self.set(self.get().append(value))
+        """Append a value to the option value.
+        """
+        v = self.get() or []
+        v.append(value)
+        self.set(v)
 
     def short_opt(self):
+        """Short option spec.
+        """
         if self.short:
             if self.val:
                 return self.short + ':'
@@ -45,6 +78,8 @@ class Opt:
             return None
 
     def long_opt(self):
+        """Long option spec.
+        """
         if self.long:
             if self.val:
                 return self.long + '='
@@ -68,6 +103,12 @@ class Opt:
             print '\tDefault', self.default or 'None'
 
     def specify(self, k, v):
+        """Specify the option. Called when the option is set
+        from the command line.
+
+        k  option switch used
+        v  optional value given (if any)
+        """
         if k in self.optkeys:
             if self.val is None and v:
                 self.opts.err("Option '%s' does not take a value" % k)
@@ -80,52 +121,113 @@ class Opt:
             return 0
 
     def specified(self):
+        """Test whether the option has been specified: set
+        from the command line.
+        """
         return self.specified_opt
 
+class OptVals:
+    """Class to hold option values.
+    """
+    pass
+
 class Opts:
+    """Container for options.
+    """
     def __init__(self, use=None):
-        self._usage = use
-        self._options = []
-        self._options_map = {}
-        self._argv = []
-        self._vals = {}
-        self._globals = {}
-        self._locals = {}
-        self.quiet = 0
+        """Options constructor.
+
+        use  usage string
+        """
+        self.use = use
+        # List of options.
+        self.options = []
+        # Options indexed by name.
+        self.options_map = {}
+        # Command-line arguments.
+        self.argv = []
+        # Option values.
+        self.vals = OptVals()
+        self.vals.quiet = 0
+        # Variables for default scripts.
+        self.vars = {}
+
+    def __repr__(self):
+        return '\n'.join(map(str, self.options))
+
+    __str__ = __repr__
 
     def opt(self, name, **args):
+        """Add an option.
+
+        name    option name
+        **args  keyword params for option constructor
+        """
         x = Opt(self, name, **args)
-        self._options.append(x)
-        self._options_map[name] = x
+        self.options.append(x)
+        self.options_map[name] = x
         return x
 
+    def setvar(self, var, val):
+        """Set a default script variable.
+        """
+        self.vars[var] = val
+
+    def getvar(self, var):
+        """Get a default script variable.
+        """
+        return self.vars.get(var)
+
+    def option(self, name):
+        """Get an option (object).
+        """
+        return self.options_map.get(name)
+
+    def setopt(self, name, val):
+        """Set an option value.
+        An option can also be set using 'opts.vals.name = val'.
+        """
+        setattr(self.vals, name, val)
+
     def getopt(self, name):
-        return self._options_map.get(name)
+        """Get an option value.
+        An option value can also be got using 'opts.vals.name'.
+        """
+        getattr(self.vals, name)
 
     def specified(self, name):
-        opt = self.getopt(name)
+        """Test if an option has been specified.
+        """
+        opt = self.option(name)
         return opt and opt.specified()
 
-    def setvar(self, name, val):
-        self._globals[name] = val
-
     def err(self, msg):
+        """Print an error to stderr and exit.
+        """
         print >>sys.stderr, "Error:", msg
         sys.exit(1)
 
     def info(self, msg):
-        if self.quiet: return
+        """Print a message to stdout (unless quiet is set).
+        """
+        if self.vals.quiet: return
         print msg
 
     def warn(self, msg):
+        """Print a warning to stdout.
+        """
         print >>sys.stderr, "Warning:", msg
 
     def parse(self, argv):
-        self._argv = argv
+        """Parse arguments argv using the options.
+
+        return remaining arguments
+        """
+        self.argv = argv
         (vals, args) = getopt(argv[1:], self.short_opts(), self.long_opts())
-        self._args = args
+        self.args = args
         for (k, v) in vals:
-            for opt in self._options:
+            for opt in self.options:
                 if opt.specify(k, v): break
             else:
                 print >>sys.stderr, "Error: Unknown option:", k
@@ -133,63 +235,92 @@ class Opts:
         return args
 
     def short_opts(self):
+        """Get short options specifier for getopt.
+        """
         l = []
-        for x in self._options:
+        for x in self.options:
             y = x.short_opt()
             if not y: continue
             l.append(y)
         return ''.join(l)
 
     def long_opts(self):
+        """Get long options specifier for getopt.
+        """
         l = []
-        for x in self._options:
+        for x in self.options:
             y = x.long_opt()
             if not y: continue
             l.append(y)
-        return ''.join(l)
+        return l
 
     def usage(self):
-        print 'Usage: ', self._argv[0], self._usage or 'OPTIONS'
-        for opt in self._options:
+        print 'Usage: ', self.argv[0], self.use or 'OPTIONS'
+        for opt in self.options:
             opt.show()
 
     def load_defaults(self):
-        for x in [ '' ] + self.path.split(':'):
+        """Load a defaults script. Assumes these options set:
+        'path'    search path
+        'default' script name
+        """
+        for x in [ '' ] + self.vals.path.split(':'):
             if x:
-                p = os.path.join(x, self.defaults)
+                p = os.path.join(x, self.vals.defaults)
             else:
-                p = self.defaults
+                p = self.vals.defaults
             if os.path.exists(p):
                 self.load(p)
                 break
         else:
             self.err("Cannot open defaults file %s" % self.defaults)
 
-    def load(self, defaults):
-        self._globals['sys'] = sys
-        self._globals['config_file'] = defaults
-        execfile(defaults, self._globals, self._locals)
+    def load(self, defaults, help=0):
+        """Load a defaults file. Local variables in the file
+        are used to set options with the same names.
+        Variables are not used to set options that are already specified.
+        """
+        # Create global and lobal dicts for the file.
+        # Initialize locals to the vars.
+        # Use exec to do the standard imports and
+        # define variables we are passing to the script.
+        globals = {}
+        locals = {}
+        locals.update(self.vars)
+        cmd = '\n'.join(["import sys",
+                         "import os",
+                         "import os.path",
+                         "xm_file = '%s'" % defaults,
+                         "xm_help = %d" % help ])
+        exec cmd in globals, locals
+        execfile(defaults, globals, locals)
+        if help: return
+        # Extract the values set by the script and set the corresponding
+        # options, if not set on the command line.
         vtypes = [ types.StringType,
                    types.ListType,
                    types.IntType,
                    types.FloatType
                    ]
-        for (k, v) in self._locals.items():
+        for (k, v) in locals.items():
             if self.specified(k): continue
             if not(type(v) in vtypes): continue
-            print 'SET ', k, v
-            setattr(self, k, v)
+            self.setopt(k, v)
 
 def set_true(opt, k, v):
+    """Set an option true."""
     opt.set(1)
 
 def set_false(opt, k, v):
+    """Set an option false."""
     opt.set(0)
 
 def set_value(opt, k, v):
+    """Set an option to a valoue."""
     opt.set(v)
 
 def set_int(opt, k, v):
+    """Set an option to an integer value."""
     try:
         v = int(v)
     except:
@@ -197,4 +328,12 @@ def set_int(opt, k, v):
     opt.set(v)
 
 def append_value(opt, k, v):
+    """Append a value to a list option."""
     opt.append(v)
+
+def set_var(opt, k, v):
+    """Set a default script variable.
+    """
+    (var, val) = v.strip().split('=')
+    opt.opts.setvar(var.strip(), val.strip())
+
index 0108a8df0ffcda6dafa0bf3af0b35167b0e2dd71..90fff21f6ad6d03514673e30ca32cf1481ec370b 100644 (file)
@@ -1,3 +1,6 @@
+# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+"""Domain shutdown.
+"""
 import string
 import sys
 import time